fastlane-plugin-xcconfig_actions 1.1.0 → 1.2.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/README.md +38 -0
- data/lib/fastlane/plugin/xcconfig_actions/actions/build_settings_to_flags_action.rb +122 -0
- data/lib/fastlane/plugin/xcconfig_actions/actions/read_xcconfig_action.rb +21 -12
- data/lib/fastlane/plugin/xcconfig_actions/actions/validate_xcconfig_action.rb +5 -1
- data/lib/fastlane/plugin/xcconfig_actions/helper/scripts/parse.py +18 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/scripts/scrape.py +23 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcconfig_actions_helper.rb +10 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspec.rb +259 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Clang LLVM 1.0.xcspec +5888 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/CoreBuildSystem.xcspec +4022 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Ld.xcspec +817 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.1/Swift.xcspec +854 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Clang LLVM 1.0.xcspec +6001 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/CoreBuildSystem.xcspec +4050 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Ld.xcspec +868 -0
- data/lib/fastlane/plugin/xcconfig_actions/helper/xcspecs/10.2/Swift.xcspec +886 -0
- data/lib/fastlane/plugin/xcconfig_actions/version.rb +1 -1
- metadata +44 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ff5347f5b29ae989604b428568e7a6f6f76a782699a2d6f0afdb1696f9d41ce
|
4
|
+
data.tar.gz: ccabe7e926c527995f7d648916ff5113d510cb9099ea9d312fd49a8db3c08573
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5cf33cdd535fe366170caafe3b6d9a46262dcf9273f14b4c3fa3cebdefd84989d60f59ee59e5e189f394186b5d98666def29db384f753f1e8c62919060dfd66
|
7
|
+
data.tar.gz: 510bed17f4306c2d6aaac5c48992554846ecb19d724574a701a0244fdf32c1fd0d13c3a0baf79bb714459683ee9a643c7f1309ce51353b031185c7c40df75dc9
|
data/README.md
CHANGED
@@ -34,6 +34,41 @@ Things **not supported** at the moment:
|
|
34
34
|
- Use of `<DEVELOPER_DIR>` in include paths
|
35
35
|
- Use of curly braces in variable references, e.g. `${VAR}`
|
36
36
|
|
37
|
+
The build settings are also saved as a dictionary under `SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS` key in current `lane_context`.
|
38
|
+
|
39
|
+
### build_settings_to_flags
|
40
|
+
|
41
|
+
Map build settings to Clang Cxx/Objective-C compiler, Swift compiler and Linker flags.
|
42
|
+
|
43
|
+
This action is useful when you plan to reuse xcconfigs with other tools, such as [Buck](https://buckbuild.com/), and you want to translate xcconfigs into the compiler/linker flags.
|
44
|
+
|
45
|
+
Build flags can be printed to standard output or saved to file.
|
46
|
+
The flags are also available via lane context as `lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_FLAGS]`.
|
47
|
+
|
48
|
+
The result is a dictionary with following keys:
|
49
|
+
|
50
|
+
- `compiler_flags` for Clang CXX/Objective-C compiler.
|
51
|
+
- `swift_compiler_flags` for Swift compiler.
|
52
|
+
- `linker_flags` for Clang linker.
|
53
|
+
|
54
|
+
<!-- TODO: Add info on how it works. -->
|
55
|
+
|
56
|
+
References:
|
57
|
+
|
58
|
+
- [Xcode Build Settings](https://help.apple.com/xcode/mac/10.2/#/itcaec37c2a6)
|
59
|
+
- [LLVM Clang Command Line Options](https://clang.llvm.org/docs/ClangCommandLineReference.html)
|
60
|
+
- [LLVM Clang Diagnostics](https://clang.llvm.org/docs/DiagnosticsReference.html)
|
61
|
+
- [GCC Warning Options](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html)
|
62
|
+
- https://gist.github.com/fabiopelosin/4560417
|
63
|
+
- https://pewpewthespells.com/blog/xcode_build_system.html
|
64
|
+
|
65
|
+
#### Known Issues
|
66
|
+
|
67
|
+
Flags like `-sdk iphoneos` and `-isysroot iphoneos` may not be suitable for all uses, so may have to remove them from all flags.
|
68
|
+
|
69
|
+
The flag like `-std=gnu++14` is added to `compiler_flags` but it's not applicable for Objective-C code.
|
70
|
+
Most tools have differentiation between C flags (C and Objective-C) and Cxx flags (C++/Objective-C++).
|
71
|
+
|
37
72
|
### validate_xcconfig
|
38
73
|
|
39
74
|
Validate xcconfig using set of very opinionated rules:
|
@@ -96,6 +131,9 @@ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plu
|
|
96
131
|
# Read xcconfig example.
|
97
132
|
bundle exec fastlane read
|
98
133
|
|
134
|
+
# Map build settings to build flags.
|
135
|
+
bundle exec fastlane build_flags
|
136
|
+
|
99
137
|
# Validate xcconfig example.
|
100
138
|
bundle exec fastlane validate
|
101
139
|
```
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../helper/xcconfig_actions_helper'
|
3
|
+
require_relative '../helper/xcspec'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Actions
|
7
|
+
ActionHelper = Fastlane::Helper::XcconfigActionsHelper
|
8
|
+
Xcspec = Fastlane::Helper::Xcspec
|
9
|
+
|
10
|
+
module SharedValues
|
11
|
+
XCCONFIG_ACTIONS_BUILD_FLAGS = :XCCONFIG_ACTIONS_BUILD_FLAGS
|
12
|
+
end
|
13
|
+
|
14
|
+
class BuildSettingsToFlagsAction < Action
|
15
|
+
def self.run(params)
|
16
|
+
build_settings = params[:build_settings] || Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS]
|
17
|
+
UI.user_error!("Missing build settings input") unless build_settings
|
18
|
+
xcode = params[:xcode]
|
19
|
+
|
20
|
+
clang_spec = load_tool_spec("Clang*", xcode: xcode)
|
21
|
+
swift_spec = load_tool_spec("Swift*", xcode: xcode)
|
22
|
+
linker_spec = load_tool_spec("Ld*", xcode: xcode)
|
23
|
+
|
24
|
+
clang_mapping = clang_spec.map_build_settings(build_settings)
|
25
|
+
swift_mapping = swift_spec.map_build_settings(build_settings)
|
26
|
+
linker_mapping = linker_spec.map_build_settings(build_settings)
|
27
|
+
|
28
|
+
flags = {
|
29
|
+
"compiler_flags" => clang_mapping.flags,
|
30
|
+
"swift_compiler_flags" => swift_mapping.flags,
|
31
|
+
"linker_flags" => [
|
32
|
+
linker_mapping.flags,
|
33
|
+
linker_mapping.linker_flags,
|
34
|
+
clang_mapping.linker_flags,
|
35
|
+
swift_mapping.linker_flags
|
36
|
+
].reject(&:empty?).join(" ")
|
37
|
+
}
|
38
|
+
|
39
|
+
Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_FLAGS] = flags
|
40
|
+
|
41
|
+
if params[:output_path]
|
42
|
+
File.open(params[:output_path], "w") { |f| f.puts(flags.to_json) }
|
43
|
+
else
|
44
|
+
return flags
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
###
|
49
|
+
# @!group Xcspecs
|
50
|
+
###
|
51
|
+
|
52
|
+
def self.load_tool_spec(tool, xcode:)
|
53
|
+
core_spec_path = ActionHelper.find_xcspec("CoreBuildSystem*", xcode: xcode)
|
54
|
+
standard_spec = Xcspec.new(core_spec_path, id: "com.apple.buildsettings.standard")
|
55
|
+
core_spec = Xcspec.new(core_spec_path, id: "com.apple.build-system.core")
|
56
|
+
|
57
|
+
Xcspec.new(
|
58
|
+
ActionHelper.find_xcspec(tool, xcode: xcode),
|
59
|
+
standard_spec: standard_spec,
|
60
|
+
core_spec: core_spec
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
###
|
65
|
+
# @!group Info and Options
|
66
|
+
###
|
67
|
+
|
68
|
+
def self.description
|
69
|
+
"Map xcconfig build settings to compiler and linker build flags"
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.authors
|
73
|
+
["Maksym Grebenets"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.return_value
|
77
|
+
"Build flags dictionary"
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.details
|
81
|
+
[
|
82
|
+
"Build flags keys:",
|
83
|
+
"- compiler_flags: CXX compiler flags for clang compiler",
|
84
|
+
"- swift_compiler_flags: Compiler flags for Swift compiler",
|
85
|
+
"- linker_flags: Linker flags for clang linker (Cxx and Swift)"
|
86
|
+
].join("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.category
|
90
|
+
:building
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.available_options
|
94
|
+
[
|
95
|
+
FastlaneCore::ConfigItem.new(key: :build_settings,
|
96
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_BUILD_SETTINGS",
|
97
|
+
description: "Build settings to convert to build flags",
|
98
|
+
optional: true,
|
99
|
+
type: Hash,
|
100
|
+
verify_block: proc do |value|
|
101
|
+
UI.user_error!("Missing build settings") if value.nil?
|
102
|
+
end),
|
103
|
+
FastlaneCore::ConfigItem.new(key: :xcode,
|
104
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_XCODE",
|
105
|
+
description: "Xcode version of path to Xcode.app",
|
106
|
+
optional: true,
|
107
|
+
default_value: "10.2",
|
108
|
+
type: String),
|
109
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
110
|
+
env_name: "XCCONFIG_ACTIONS_BUILD_FLAGS_OUTPUT_PATH",
|
111
|
+
description: "Output path to save build settings JSON",
|
112
|
+
optional: true,
|
113
|
+
type: String)
|
114
|
+
]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.is_supported?(platform)
|
118
|
+
[:ios, :mac].include?(platform)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -4,6 +4,10 @@ require_relative '../helper/xcconfig_actions_helper'
|
|
4
4
|
|
5
5
|
module Fastlane
|
6
6
|
module Actions
|
7
|
+
module SharedValues
|
8
|
+
XCCONFIG_ACTIONS_BUILD_SETTINGS = :XCCONFIG_ACTIONS_BUILD_SETTINGS
|
9
|
+
end
|
10
|
+
|
7
11
|
class ReadXcconfigAction < Action
|
8
12
|
def self.run(params)
|
9
13
|
path = params[:path]
|
@@ -13,9 +17,7 @@ module Fastlane
|
|
13
17
|
|
14
18
|
config = read_config(path)
|
15
19
|
|
16
|
-
|
17
|
-
json = config.to_json
|
18
|
-
else
|
20
|
+
unless params[:no_resolve]
|
19
21
|
parent_config = read_config(parent)
|
20
22
|
|
21
23
|
parent_config["SRCROOT"] = srcroot
|
@@ -24,20 +26,22 @@ module Fastlane
|
|
24
26
|
if Helper::XcconfigActionsHelper.command_exist?("xcodebuild")
|
25
27
|
# Set value of XCODE_VERSION_MAJOR not available when reading xcconfigs directly.
|
26
28
|
xcode_version = `xcodebuild -version | head -n1 | cut -d' ' -f2 | xargs`.strip
|
27
|
-
|
28
|
-
parent_config["XCODE_VERSION_MAJOR"] =
|
29
|
+
xcode_version_major_padded = xcode_version.split(".").first.rjust(2, "0") + "00"
|
30
|
+
parent_config["XCODE_VERSION_MAJOR"] = xcode_version_major_padded
|
29
31
|
end
|
30
32
|
|
31
33
|
resolved_parent_config = resolve_config(parent_config)
|
32
34
|
resolved_config = resolve_config(config, parent: resolved_parent_config)
|
33
35
|
|
34
|
-
|
36
|
+
config = resolved_parent_config.merge(resolved_config)
|
35
37
|
end
|
36
38
|
|
39
|
+
Actions.lane_context[SharedValues::XCCONFIG_ACTIONS_BUILD_SETTINGS] = config
|
40
|
+
|
37
41
|
if params[:output_path]
|
38
|
-
File.open(params[:output_path], "w") { |f| f.puts(
|
42
|
+
File.open(params[:output_path], "w") { |f| f.puts(config.to_json) }
|
39
43
|
else
|
40
|
-
return
|
44
|
+
return config
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
@@ -96,6 +100,7 @@ module Fastlane
|
|
96
100
|
def self.resolve_value(value, key:, resolved: {}, parent: {})
|
97
101
|
matches = value.scan(/(\$\([^$\)]*\))/)
|
98
102
|
|
103
|
+
mutable_value = value.dup # Prevent unwanted side-effect of input modification.
|
99
104
|
matches.each do |group|
|
100
105
|
group.each do |match|
|
101
106
|
var_name = match.delete("$()")
|
@@ -105,13 +110,13 @@ module Fastlane
|
|
105
110
|
else
|
106
111
|
resolved[var_name] || parent[var_name]
|
107
112
|
end
|
108
|
-
|
109
|
-
resolved[key] =
|
113
|
+
mutable_value.gsub!(match, var_value || "")
|
114
|
+
resolved[key] = mutable_value
|
110
115
|
end
|
111
116
|
end
|
112
117
|
|
113
118
|
# If there are still variables, keep resolving then.
|
114
|
-
|
119
|
+
mutable_value.include?("$(") ? resolve_value(mutable_value, key: key, resolved: resolved, parent: parent) : mutable_value
|
115
120
|
end
|
116
121
|
|
117
122
|
# Resolve xcconfig values using parent config.
|
@@ -147,6 +152,10 @@ module Fastlane
|
|
147
152
|
""
|
148
153
|
end
|
149
154
|
|
155
|
+
def self.category
|
156
|
+
:building
|
157
|
+
end
|
158
|
+
|
150
159
|
def self.available_options
|
151
160
|
[
|
152
161
|
FastlaneCore::ConfigItem.new(key: :path,
|
@@ -190,7 +199,7 @@ module Fastlane
|
|
190
199
|
end
|
191
200
|
|
192
201
|
def self.is_supported?(platform)
|
193
|
-
|
202
|
+
[:ios, :mac].include?(platform)
|
194
203
|
end
|
195
204
|
end
|
196
205
|
end
|
@@ -91,6 +91,10 @@ module Fastlane
|
|
91
91
|
].join("\n")
|
92
92
|
end
|
93
93
|
|
94
|
+
def self.category
|
95
|
+
:linting
|
96
|
+
end
|
97
|
+
|
94
98
|
def self.available_options
|
95
99
|
[
|
96
100
|
FastlaneCore::ConfigItem.new(key: :path,
|
@@ -113,7 +117,7 @@ module Fastlane
|
|
113
117
|
end
|
114
118
|
|
115
119
|
def self.is_supported?(platform)
|
116
|
-
|
120
|
+
[:ios, :mac].include?(platform)
|
117
121
|
end
|
118
122
|
end
|
119
123
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from bs4 import BeautifulSoup
|
2
|
+
import re
|
3
|
+
|
4
|
+
soup = BeautifulSoup(open("page.html"), "html.parser")
|
5
|
+
|
6
|
+
entries = soup.find_all("div", id=re.compile("dev.*"))
|
7
|
+
|
8
|
+
title_re = re.compile(".*\((.*)\)")
|
9
|
+
settings = []
|
10
|
+
for e in entries:
|
11
|
+
text = e.find("h2", class_="Name").text
|
12
|
+
m = title_re.match(text)
|
13
|
+
if m == None:
|
14
|
+
settings.append(text)
|
15
|
+
else:
|
16
|
+
settings.append(m.group(1))
|
17
|
+
|
18
|
+
print settings
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# https://stackoverflow.com/questions/8049520/web-scraping-javascript-page-with-python
|
2
|
+
from selenium import webdriver
|
3
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
4
|
+
from selenium.webdriver.support import expected_conditions
|
5
|
+
from selenium.webdriver.common.by import By
|
6
|
+
|
7
|
+
options = webdriver.FirefoxOptions()
|
8
|
+
options.headless = True
|
9
|
+
driver = webdriver.Firefox(firefox_options=options)
|
10
|
+
|
11
|
+
id = "itcaec37c2a6"
|
12
|
+
url = "https://help.apple.com/xcode/mac/10.2/#/{}".format(id)
|
13
|
+
driver.get(url)
|
14
|
+
|
15
|
+
# https://stackoverflow.com/questions/26566799/wait-until-page-is-loaded-with-selenium-webdriver-for-python
|
16
|
+
WebDriverWait(driver, 10).until(
|
17
|
+
expected_conditions.text_to_be_present_in_element(
|
18
|
+
(By.ID, id),
|
19
|
+
'Build settings reference'
|
20
|
+
)
|
21
|
+
)
|
22
|
+
|
23
|
+
print driver.page_source.encode('utf-8').strip()
|
@@ -42,6 +42,16 @@ module Fastlane
|
|
42
42
|
includes: includes
|
43
43
|
}
|
44
44
|
end
|
45
|
+
|
46
|
+
def self.find_xcspec(name, xcode:)
|
47
|
+
search_path = File.exist?(xcode) ? File.join(xcode, "Contents/Plugins") : File.join(File.dirname(__FILE__), "xcspecs", xcode)
|
48
|
+
UI.user_error!("Can't find app path of xcspecs folder for xcode: #{xcode}") unless File.exist?(search_path)
|
49
|
+
|
50
|
+
query = File.join(search_path, "**", name + ".xcspec")
|
51
|
+
xcspec = Dir[query].first
|
52
|
+
UI.user_error!("Can't find xcspec with name: #{name}") unless File.exist?(xcspec)
|
53
|
+
xcspec
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require "plist"
|
2
|
+
require "nokogiri-plist"
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
UI = UI unless Fastlane.const_defined?("UI")
|
6
|
+
|
7
|
+
module Helper
|
8
|
+
# Xcspec helper class.
|
9
|
+
class Xcspec
|
10
|
+
###
|
11
|
+
# @!group Mapping
|
12
|
+
###
|
13
|
+
class Mapping
|
14
|
+
attr_reader :flags, :linker_flags
|
15
|
+
|
16
|
+
def initialize(flags = "", linker_flags = "")
|
17
|
+
@flags = flags
|
18
|
+
@linker_flags = linker_flags
|
19
|
+
end
|
20
|
+
|
21
|
+
# Join with other mapping and return new mapping.
|
22
|
+
def join(other)
|
23
|
+
return self if other.nil?
|
24
|
+
|
25
|
+
joined_flags = [flags, other.flags].reject(&:empty?).join(" ")
|
26
|
+
joined_linker_flags = [linker_flags, other.linker_flags].reject(&:empty?).join(" ")
|
27
|
+
|
28
|
+
Mapping.new(joined_flags, joined_linker_flags)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
###
|
33
|
+
# @!group Initialization
|
34
|
+
###
|
35
|
+
|
36
|
+
attr_reader :tool
|
37
|
+
|
38
|
+
def initialize(path, id: nil, standard_spec: nil, core_spec: nil)
|
39
|
+
UI.user_error!("No such file: #{path}") unless path && File.exist?(path)
|
40
|
+
|
41
|
+
@plist = Xcspec.load_plist(path)
|
42
|
+
@tool = find_tool(id)
|
43
|
+
|
44
|
+
all_tools = [@tool]
|
45
|
+
all_tools << standard_spec.tool if standard_spec
|
46
|
+
all_tools << core_spec.tool if core_spec
|
47
|
+
|
48
|
+
@all_options = all_tools.flat_map { |t| t["Options"] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.load_plist(path)
|
52
|
+
file_type = `file -b --mime-type #{path.shellescape}`.chomp
|
53
|
+
if file_type == "text/xml" || file_type == "application/xml"
|
54
|
+
xml_plist = path
|
55
|
+
else
|
56
|
+
if FastlaneCore::Helper.mac?
|
57
|
+
xml_plist = Tempfile.new("xcspec.plist").path
|
58
|
+
result = system("plutil -convert xml1 -o #{xml_plist.shellescape} #{path.shellescape}")
|
59
|
+
UI.user_error!("Couldn't convert #{path} xcspec to XML plist") unless result
|
60
|
+
else
|
61
|
+
# There is plist-utils library, but it can only convert binary to XML, can't handle ASCII.
|
62
|
+
UI.user_error!("Can't convert ASCII plists to XML on Linux or Windows platform")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Nokogiri::PList(File.open(xml_plist))
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_tool(id)
|
70
|
+
return @plist unless @plist.kind_of?(Array)
|
71
|
+
id ? @plist.find { |t| t["Identifier"] == id } : @plist.first
|
72
|
+
end
|
73
|
+
|
74
|
+
###
|
75
|
+
# @!group Mapping
|
76
|
+
###
|
77
|
+
|
78
|
+
def map_build_settings(build_settings)
|
79
|
+
# Build settings provided for mapping will not include all possible build settings.
|
80
|
+
# Add default values for missing build settings.
|
81
|
+
missing_build_settings = @all_options.reduce({}) do |memo, opt|
|
82
|
+
name = opt["Name"]
|
83
|
+
build_settings.key?(name) ? memo : memo.merge({ name => opt["DefaultValue"] })
|
84
|
+
end
|
85
|
+
complete_build_settings = build_settings.merge(missing_build_settings)
|
86
|
+
|
87
|
+
mappings = build_settings.flat_map do |setting, value|
|
88
|
+
map_build_setting_value(setting, value, complete_build_settings)
|
89
|
+
end.compact
|
90
|
+
|
91
|
+
mappings.reduce(Mapping.new) { |memo, m| memo.join(m) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def map_build_setting_value(name, value, build_settings)
|
95
|
+
option = @tool["Options"].find { |o| o["Name"] == name }
|
96
|
+
return nil unless option
|
97
|
+
|
98
|
+
map_option(option, value, build_settings)
|
99
|
+
end
|
100
|
+
|
101
|
+
def map_option(option, value, build_settings)
|
102
|
+
# Evaluate and check the 'Condition'.
|
103
|
+
return nil unless check_condition(option, build_settings)
|
104
|
+
|
105
|
+
# If type of the value is one of List types (StringList, PathList),
|
106
|
+
# then split it into value list, else just use scalar value itself.
|
107
|
+
scalar_values = option["Type"].downcase.end_with?("list") ? value.split : [value]
|
108
|
+
scalar_values.flat_map { |v| map_option_scalar_value(option, v, build_settings) }.compact
|
109
|
+
end
|
110
|
+
|
111
|
+
def map_option_scalar_value(option, value, build_settings)
|
112
|
+
# At this point we deal with scalar value.
|
113
|
+
|
114
|
+
# Type = StringList
|
115
|
+
# - CommandLineFlag and CommandLinePrefixFlag - map each string in the list.
|
116
|
+
# - CommandLineArgs - map each string in the list.
|
117
|
+
# - Finally there are entries with only Category = CustomFlags.
|
118
|
+
# Those end up in compiler flags for sure, so looks like if category is CustomFlags,
|
119
|
+
# then should use them as is.
|
120
|
+
# There's only 3 of them: OTHER_CFLAGS, OTHER_CPLUSPLUSFLAGS and WARNING_CFLAGS.
|
121
|
+
# PathList, that for all intents and purposes can be handed as StringList.
|
122
|
+
|
123
|
+
# - CommandLineArgs
|
124
|
+
# - If input is StringList, then map each entry from string list according to CommandLineArgs value
|
125
|
+
# The value of CommandLineArgs can be an array, use flat map
|
126
|
+
# Can have a switch, e.g.
|
127
|
+
# CommandLineArgs = {
|
128
|
+
# "" = ();
|
129
|
+
# "<<otherwise>>" = (
|
130
|
+
# "-$(DEPLOYMENT_TARGET_CLANG_FLAG_NAME)=$(value)",
|
131
|
+
# );
|
132
|
+
# };
|
133
|
+
# All these switches, however, map to nothing for empty string and to something for non-empty string.
|
134
|
+
# The vey same switch is used for enums. Switching for enums vs string is no different,
|
135
|
+
# since switching happens on string values.
|
136
|
+
# <<otherwise>> is for other values
|
137
|
+
# - AdditionalLinkerArgs
|
138
|
+
# Few build settings have them, collected separately, work just like CommandLineArgs for parsing.
|
139
|
+
# - CommandLineFlag and CommandLinePrefixFlag
|
140
|
+
# - If input is StringList then applied to each entry in the list, to each entry applied as to a scalar value:
|
141
|
+
# - If input is scalar String value, then applied just once as -<flag> $(value)
|
142
|
+
# - If input is boolean, then just the flag is used, e.g. -<flag>, no Prefix option supported for boolean.
|
143
|
+
# - While for prefix it appears there's no space and it is -<prefix>$(value)
|
144
|
+
# - An exception(!) is MACH_O_TYPE, where no value is appended but just the flag is used.
|
145
|
+
# It is also the only enum where list of values has command line flag for each value.
|
146
|
+
|
147
|
+
# Type = Enumeration
|
148
|
+
# Most of them have CommandLineArgs with a switch.
|
149
|
+
# Some also come with AdditionalLinkerArgs, which is mapped in similar way.
|
150
|
+
# Then there's MACH_O_TYPE, which has CommandLineFlag under Values...
|
151
|
+
# But only when used for linker flags:
|
152
|
+
# Value = "mh_dylib";
|
153
|
+
# CommandLineFlag = "-dynamiclib";
|
154
|
+
# -dynamiclib is a flag that doesn't take input, so only specified as is.
|
155
|
+
|
156
|
+
flags = ""
|
157
|
+
if (cli_args = option["CommandLineArgs"])
|
158
|
+
flags = map_args(option, cli_args, value, build_settings)
|
159
|
+
elsif (cli_flag = option["CommandLineFlag"])
|
160
|
+
# If Boolean and YES, then use CommandLineFlag value.
|
161
|
+
if option["Type"] == "Boolean"
|
162
|
+
flags = cli_flag if value == "YES"
|
163
|
+
else
|
164
|
+
flags = [cli_flag, value].join(" ")
|
165
|
+
end
|
166
|
+
elsif (cli_prefix_flag = option["CommandLinePrefixFlag"])
|
167
|
+
flags = [cli_prefix_flag, value].join # Join with no space in between.
|
168
|
+
elsif option["Category"] == "CustomFlags"
|
169
|
+
flags = value
|
170
|
+
else
|
171
|
+
# Nothing to map to, except for when it's MACH_O_TYPE, when it has list of dictionaries:
|
172
|
+
# Values = ( { Value = "v", CommandLineFlag = "f", ... } )
|
173
|
+
match = (option["Values"] || []).find { |v| v["Value"] == value } || {}
|
174
|
+
flags = match["CommandLineFlag"] || ""
|
175
|
+
end
|
176
|
+
|
177
|
+
linker_flags = ""
|
178
|
+
if (linker_args = option["AdditionalLinkerArgs"])
|
179
|
+
linker_flags = map_args(option, linker_args, value, build_settings)
|
180
|
+
end
|
181
|
+
|
182
|
+
Mapping.new(flags, linker_flags)
|
183
|
+
end
|
184
|
+
|
185
|
+
def map_args(option, args, value, build_settings)
|
186
|
+
return "" unless args
|
187
|
+
|
188
|
+
if args.kind_of?(Hash)
|
189
|
+
map_args(option, args[value] || args["<<otherwise>>"], value, build_settings)
|
190
|
+
else
|
191
|
+
# Args should be an array here, but can be just single string.
|
192
|
+
|
193
|
+
resolved_args = args.kind_of?(Array) ? args.dup : [args]
|
194
|
+
# Replacing $(value) is just one part, need to resolve any build settings present too.
|
195
|
+
resolved_args = resolved_args.map { |a| a.gsub("$(value)", value) }.join(" ")
|
196
|
+
Fastlane::Actions::ReadXcconfigAction.resolve_value(
|
197
|
+
resolved_args,
|
198
|
+
key: "resolved_args",
|
199
|
+
resolved: {},
|
200
|
+
parent: build_settings
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Constants for condition evaluation.
|
206
|
+
NO = false
|
207
|
+
YES = true
|
208
|
+
|
209
|
+
# Evaluate and check the condition.
|
210
|
+
# Conditions come in form like this:
|
211
|
+
# "$(COMPILER_INDEX_STORE_ENABLE) == YES || ( $(COMPILER_INDEX_STORE_ENABLE) == Default && $(GCC_OPTIMIZATION_LEVEL) == 0 )"
|
212
|
+
# Return true if there's no condition to evaluate.
|
213
|
+
def check_condition(option, build_settings)
|
214
|
+
condition = option["Condition"]
|
215
|
+
return true unless condition
|
216
|
+
|
217
|
+
# Need to resolve all $(VAR) references using build settings.
|
218
|
+
# At this point using complete build settings,
|
219
|
+
# which include default values for all known build settings.
|
220
|
+
|
221
|
+
# Handy that read_xcconfig action already has a helper to resolve a value.
|
222
|
+
# Just pass current build settings as parent config.
|
223
|
+
resolved_condition = Fastlane::Actions::ReadXcconfigAction.resolve_value(
|
224
|
+
condition,
|
225
|
+
key: "condition",
|
226
|
+
resolved: {},
|
227
|
+
parent: build_settings
|
228
|
+
)
|
229
|
+
|
230
|
+
# Resolved condition is now a C-like expression, which also can be evaluated as Ruby code.
|
231
|
+
# With small changes though.
|
232
|
+
# All literals in condition can be treated as strings, except for YES/NO boolean literals,
|
233
|
+
# which are replaced with `true` and `false`.
|
234
|
+
# However, xcspecs are very inconsistent when it comes to strings.
|
235
|
+
# Some values are used unquoted in the conditions, such as:
|
236
|
+
# Defatult, mh_object, bitcode.
|
237
|
+
# There's also use of '' and \"\" for empty string, the latter may cause issues.
|
238
|
+
# Then "same-as-input" that may have to be resolved - do not handle for now.
|
239
|
+
# Finally, $(variant) == profile - just leave it for now.
|
240
|
+
|
241
|
+
# Ways to fix.
|
242
|
+
# 1. Scan for all enums in xcspecs and define module vars for each enum value,
|
243
|
+
# so when evaluated, is replaced with variable.
|
244
|
+
# 2. Process resolved condition by wrapping all unwrapped entries in quotes.
|
245
|
+
# Using approach 2 for now with insane regex.
|
246
|
+
|
247
|
+
# In all cases replace \"\" with ''.
|
248
|
+
resolved_condition.gsub!('"', "'")
|
249
|
+
|
250
|
+
# Quote everything except YES and NO.
|
251
|
+
resolved_condition = (" " + resolved_condition + " ").gsub(/\s((\w|\d|-|\+|\.)+?)\s/, " '\\1' ").gsub(/'(YES|NO)'/, '\\1')
|
252
|
+
|
253
|
+
# rubocop:disable Security/Eval
|
254
|
+
eval(resolved_condition)
|
255
|
+
# rubocop:enable Security/Eval
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|