xcode-archive-cache 0.0.6

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.
@@ -0,0 +1,45 @@
1
+ module XcodeArchiveCache
2
+ class Command < CLAide::Command
3
+
4
+ include XcodeArchiveCache::Logs
5
+
6
+ self.abstract_command = true
7
+ self.command = "xcode-archive-cache"
8
+ self.description = "XcodeArchiveCache, cache for Xcode archive action"
9
+
10
+ def self.options
11
+ [
12
+ ["--log-level=[nothing|info|verbose]", "Level of log verbosity, defaults to info"]
13
+ ].concat(super.reject { |(name, _)| name != "--help" })
14
+ end
15
+
16
+ def self.run(argv)
17
+ help! "You cannot run XcodeArchiveCache as root." if Process.uid == 0 && !Gem.win_platform?
18
+
19
+ super(argv)
20
+ end
21
+
22
+ def initialize(argv)
23
+ super
24
+ log_level = argv.option("log-level", "info")
25
+ set_log_level(log_level)
26
+ end
27
+ end
28
+
29
+ def self.report_error(exception)
30
+ case exception
31
+ when Interrupt
32
+ info("Cancelled")
33
+ exit 1
34
+ when SystemExit
35
+ raise
36
+ else
37
+ if ENV["XCODE_ARCHIVE_CACHE_ENV"] != "development"
38
+ error(exception.message)
39
+ exit 1
40
+ else
41
+ raise exception
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,52 @@
1
+ module XcodeArchiveCache
2
+ class Command
3
+ class Inject < Command
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ self.description = "Setup workspace or project for being built using cached dependencies"
8
+
9
+ def self.options
10
+ [
11
+ ["--cachefile=/path/to/Cachefile", "Explicit Cachefile location, defaults to $PWD/Cachefile"],
12
+ ["--configuration", "Name of configuration from Cachefile, required"],
13
+ ["--destination=[<full destination specifier>|generic]", "xcodebuild-style destination. Defaults to 'generic', ignored for archive action"],
14
+ ["--storage=/path/to/storage", "Storage dir path, required"]
15
+ ].concat(super)
16
+ end
17
+
18
+ DEFAULT_CACHEFILE = "Cachefile"
19
+
20
+ def initialize(argv)
21
+ super
22
+
23
+ @configuration_name = argv.option("configuration")
24
+ if @configuration_name == nil
25
+ error("configuration is required")
26
+ exit 1
27
+ end
28
+
29
+ @storage = argv.option("storage")
30
+ if @storage == nil
31
+ error("storage parameter is required")
32
+ exit 1
33
+ end
34
+
35
+ @cachefile_path = argv.option("cachefile", DEFAULT_CACHEFILE)
36
+ @destination = argv.option("destination", XcodeArchiveCache::Xcodebuild::GENERIC_DESTINATION)
37
+ end
38
+
39
+ def run
40
+ config = XcodeArchiveCache::Config.from_file(@cachefile_path).entry
41
+ config.settings.destination = @destination
42
+ config.active_configuration_name = @configuration_name
43
+ config.storage.type = :local
44
+ config.storage.path = @storage
45
+
46
+ debug(config)
47
+
48
+ XcodeArchiveCache::Runner.new(config).run
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,211 @@
1
+ module XcodeArchiveCache
2
+ class Config
3
+
4
+ include XcodeArchiveCache::Config::DSL
5
+
6
+ class Entry
7
+
8
+ # @return [String]
9
+ #
10
+ attr_reader :name
11
+
12
+ # @return [Array<Configuration>]
13
+ #
14
+ attr_reader :configurations
15
+
16
+ # @return [String]
17
+ #
18
+ attr_accessor :active_configuration_name
19
+
20
+ # @return [Array<Target>]
21
+ #
22
+ attr_reader :targets
23
+
24
+ # @return [Settings]
25
+ #
26
+ attr_reader :settings
27
+
28
+ # @return [Storage]
29
+ #
30
+ attr_reader :storage
31
+
32
+ def initialize(name)
33
+ @name = name
34
+ @configurations = []
35
+ @active_configuration_name = nil
36
+ @settings = Settings.new
37
+ @storage = Storage.new
38
+ @targets = []
39
+ @file_extname = ""
40
+ end
41
+
42
+ # @return [String]
43
+ #
44
+ def file_path
45
+ return name if File.extname(name) == file_extname
46
+
47
+ name + file_extname
48
+ end
49
+
50
+ # @return [Configuration]
51
+ #
52
+ def active_configuration
53
+ configuration = configurations.select{|config| config.name == active_configuration_name }.first
54
+ if configuration == nil
55
+ raise Informative, "Found no configuration with name \"#{active_configuration_name}\""
56
+ end
57
+
58
+ configuration
59
+ end
60
+
61
+ def to_s
62
+ "path: #{file_path}\nactive configuration: #{active_configuration_name}\nconfigurations:\n\t#{configurations.join("\n\t")}\n#{settings}\nstorage: #{storage}\ntargets:\n\t#{targets.join("\n\t")}"
63
+ end
64
+
65
+ private
66
+
67
+ # @return [String]
68
+ #
69
+ attr_reader :file_extname
70
+ end
71
+
72
+ class Workspace < Entry
73
+ def initialize(path)
74
+ super(path)
75
+ @file_extname = ".xcworkspace"
76
+ end
77
+ end
78
+
79
+ class Project < Entry
80
+ def initialize(path)
81
+ super(path)
82
+ @file_extname = ".xcodeproj"
83
+ end
84
+ end
85
+
86
+ class Configuration
87
+ # @return [String]
88
+ #
89
+ attr_reader :name
90
+
91
+ # @return [String]
92
+ #
93
+ attr_accessor :build_configuration
94
+
95
+ # @return [String]
96
+ #
97
+ attr_accessor :action
98
+
99
+ # @return [String]
100
+ #
101
+ attr_accessor :xcodebuild_args
102
+
103
+ # @param [String] name
104
+ #
105
+ def initialize(name)
106
+ @name = name
107
+ @action = "archive"
108
+ end
109
+
110
+ def to_s
111
+ "#{name}, build configuration: #{build_configuration}, action: #{action}, xcodebuild args: \"#{xcodebuild_args}\""
112
+ end
113
+ end
114
+
115
+ class Settings
116
+ # @return [String]
117
+ #
118
+ attr_accessor :derived_data_path
119
+
120
+ # @return [String]
121
+ #
122
+ attr_accessor :destination
123
+
124
+ def to_s
125
+ "destination: #{destination}, derived data path: #{derived_data_path}"
126
+ end
127
+ end
128
+
129
+ class Storage
130
+
131
+ # @return [Symbol]
132
+ #
133
+ attr_accessor :type
134
+
135
+ # @return [String]
136
+ #
137
+ attr_accessor :path
138
+
139
+ def to_s
140
+ "#{type}, path: #{path}"
141
+ end
142
+ end
143
+
144
+ class Target
145
+
146
+ # @return [String]
147
+ #
148
+ attr_reader :name
149
+
150
+ # @return [Array<String>]
151
+ #
152
+ attr_reader :dependencies
153
+
154
+ # @param [String] name
155
+ #
156
+ def initialize(name)
157
+ @name = name
158
+ @dependencies = []
159
+ end
160
+
161
+ def to_s
162
+ "#{name}, dependencies: #{dependencies.join(", ")}"
163
+ end
164
+ end
165
+
166
+ # @param [String] path
167
+ #
168
+ def self.from_file(path)
169
+ contents = File.open(path, "r:utf-8", &:read)
170
+
171
+ config = Config.new do
172
+ begin
173
+ eval(contents, nil, path)
174
+ rescue Exception => e
175
+ raise Informative, "Invalid #{File.basename(path)} file: #{e.message}"
176
+ end
177
+ end
178
+
179
+ config
180
+ end
181
+
182
+ # @return [Entry]
183
+ #
184
+ attr_reader :entry
185
+
186
+ def initialize(&block)
187
+ @entry = nil
188
+ @current_target = nil
189
+
190
+ if block
191
+ instance_eval(&block)
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ attr_writer :entry
198
+
199
+ # @return [Configuration]
200
+ #
201
+ def current_configuration
202
+ entry.configurations.last
203
+ end
204
+
205
+ # @return [Target]
206
+ #
207
+ def current_target
208
+ entry.targets.last
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,69 @@
1
+ module XcodeArchiveCache
2
+ class Config
3
+ module DSL
4
+
5
+ # @param [String] name
6
+ #
7
+ def workspace(name)
8
+ self.entry = Workspace.new(name)
9
+
10
+ yield
11
+ end
12
+
13
+ # @param [String] name
14
+ #
15
+ def project(name)
16
+ self.entry = Project.new(name)
17
+
18
+ yield
19
+ end
20
+
21
+ # @param [String] name
22
+ #
23
+ def configuration(name)
24
+ entry.configurations.push(Configuration.new(name))
25
+
26
+ yield
27
+ end
28
+
29
+ # @param [String] name
30
+ #
31
+ def build_configuration(name)
32
+ current_configuration.build_configuration = name
33
+ end
34
+
35
+ # @param [String] name
36
+ #
37
+ def action(name)
38
+ current_configuration.action = name
39
+ end
40
+
41
+ # @param [String] args
42
+ #
43
+ def xcodebuild_args(args)
44
+ current_configuration.xcodebuild_args = args
45
+ end
46
+
47
+ # @param [String] path
48
+ #
49
+ def derived_data_path(path)
50
+ entry.settings.derived_data_path = path
51
+ end
52
+
53
+ # @return [String]
54
+ #
55
+ def target(name)
56
+ target = Target.new(name)
57
+ entry.targets.push(target)
58
+
59
+ yield
60
+ end
61
+
62
+ # @param [String] name
63
+ #
64
+ def cache(name)
65
+ current_target.dependencies.push(name)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,172 @@
1
+ module XcodeArchiveCache
2
+ module Injection
3
+ class BuildFlagsChanger
4
+
5
+ include XcodeArchiveCache::Logs
6
+
7
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
8
+ # @param [String] path
9
+ #
10
+ def add_framework_search_path(build_configuration, path)
11
+ debug("using framework search path #{path}")
12
+ add_flag_to_configuration(build_configuration, FRAMEWORK_SEARCH_PATHS_KEY, path_to_search_path(path))
13
+ end
14
+
15
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
16
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
17
+ #
18
+ def add_framework_linker_flag(build_configuration, node)
19
+ linker_flag = get_framework_linker_flag(node)
20
+ if linker_flag
21
+ debug("using linker flag #{linker_flag}")
22
+ add_linker_flag(build_configuration, linker_flag)
23
+ end
24
+ end
25
+
26
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
27
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
28
+ #
29
+ def add_framework_headers_iquote(build_configuration, artifact_location, node)
30
+ headers_search_path = get_framework_headers_iquote(artifact_location, node)
31
+ debug("using -iquote path #{headers_search_path}")
32
+ add_cflag(build_configuration, headers_search_path)
33
+ end
34
+
35
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
36
+ # @param [String] path
37
+ #
38
+ def add_library_search_path(build_configuration, path)
39
+ debug("using library search path #{path}")
40
+ add_flag_to_configuration(build_configuration, LIBRARY_SEARCH_PATHS_KEY, path_to_search_path(path))
41
+ end
42
+
43
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
44
+ # @param [String] path
45
+ #
46
+ def add_headers_search_path(build_configuration, path)
47
+ debug("using headers search path #{path}")
48
+ add_flag_to_configuration(build_configuration, HEADER_SEARCH_PATHS_KEY, path)
49
+ end
50
+
51
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
52
+ # @param [String] path
53
+ #
54
+ def add_iquote_path(build_configuration, path)
55
+ debug("using -iquote path #{path}")
56
+ add_cflag(build_configuration, path_to_iquote(path))
57
+ end
58
+
59
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
60
+ # @param [String] path
61
+ #
62
+ def add_capital_i_path(build_configuration, path)
63
+ debug("using -I path #{path}")
64
+ add_cflag(build_configuration, path_to_capital_i(path))
65
+ end
66
+
67
+ private
68
+
69
+ FRAMEWORK_SEARCH_PATHS_KEY = "FRAMEWORK_SEARCH_PATHS"
70
+ LIBRARY_SEARCH_PATHS_KEY = "LIBRARY_SEARCH_PATHS"
71
+ HEADER_SEARCH_PATHS_KEY = "HEADER_SEARCH_PATHS"
72
+ OTHER_CFLAGS_KEY = "OTHER_CFLAGS"
73
+ OTHER_LDFLAGS_KEY = "OTHER_LDFLAGS"
74
+ INHERITED_SETTINGS_VALUE = "$(inherited)"
75
+
76
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
77
+ # @param [String] flag
78
+ #
79
+ def add_linker_flag(build_configuration, flag)
80
+ add_flag_to_configuration(build_configuration, OTHER_LDFLAGS_KEY, flag)
81
+ end
82
+
83
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
84
+ # @param [String] flag
85
+ #
86
+ def add_cflag(build_configuration, flag)
87
+ add_flag_to_configuration(build_configuration, OTHER_CFLAGS_KEY, flag)
88
+ end
89
+
90
+ # @param [Xcodeproj::Project::Object::XCBuildConfiguration] build_configuration
91
+ # @param [String] key
92
+ # @param [String] flag
93
+ #
94
+ def add_flag_to_configuration(build_configuration, key, flag)
95
+ flags = build_configuration.build_settings[key]
96
+ build_configuration.build_settings[key] = add_flag(flags, flag)
97
+ end
98
+
99
+ # @param [Object] flags
100
+ # @param [String] new_flag
101
+ #
102
+ # @return [Array<String>]
103
+ #
104
+ def add_flag(flags, new_flag)
105
+ if flags && flags.length > 0
106
+ if flags.is_a?(String)
107
+ flags = [flags, new_flag]
108
+ elsif flags.is_a?(Array)
109
+ flags += [new_flag] unless flags.include?(new_flag)
110
+ else
111
+ raise StandardError.new, "Flags value is neither string nor array: #{flags.class}"
112
+ end
113
+ else
114
+ flags = [INHERITED_SETTINGS_VALUE, new_flag]
115
+ end
116
+
117
+ flags
118
+ end
119
+
120
+ # @param [String] path
121
+ #
122
+ # @return [String]
123
+ #
124
+ def path_to_search_path(path)
125
+ "\"#{path}\""
126
+ end
127
+
128
+ # @param [String] path
129
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
130
+ #
131
+ # @return [String]
132
+ #
133
+ def get_framework_headers_iquote(path, node)
134
+ if node.has_framework_product?
135
+ headers_dir_path = File.join(path, node.product_file_name, "Headers")
136
+ path_to_iquote(headers_dir_path)
137
+ end
138
+ end
139
+
140
+ # @param [String] path
141
+ #
142
+ # @return [String]
143
+ #
144
+ def path_to_iquote(path)
145
+ "-iquote \"#{path}\""
146
+ end
147
+
148
+ # @param [String] path
149
+ #
150
+ # @return [Stirng]
151
+ #
152
+ def path_to_capital_i(path)
153
+ "-I\"#{path}\""
154
+ end
155
+
156
+ # @param [XcodeArchiveCache::BuildGraph::Node] node
157
+ #
158
+ # @return [String]
159
+ #
160
+ # Something.framework -> -framework "Something"
161
+ #
162
+ def get_framework_linker_flag(node)
163
+ return unless node.product_file_name
164
+
165
+ framework_name = File.basename(node.product_file_name, File.extname(node.product_file_name))
166
+ return unless framework_name
167
+
168
+ "-framework \"#{framework_name}\""
169
+ end
170
+ end
171
+ end
172
+ end