From b908265a3e0378ff58d48c2d54a043578c2b25b3 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Mon, 18 Jul 2022 15:28:51 +0300 Subject: [PATCH] support inheritance in xcconfig options --- xcode/config_generator/config_renderer.rb | 132 ++++++++++++++++++ xcode/config_generator/example_settings.yaml | 6 +- xcode/config_generator/render_xcconfigs.rb | 120 +--------------- .../lib/touchlane/configuration_type.rb | 14 ++ 4 files changed, 152 insertions(+), 120 deletions(-) create mode 100644 xcode/config_generator/config_renderer.rb diff --git a/xcode/config_generator/config_renderer.rb b/xcode/config_generator/config_renderer.rb new file mode 100644 index 0000000..6d1a915 --- /dev/null +++ b/xcode/config_generator/config_renderer.rb @@ -0,0 +1,132 @@ +require 'json' +require 'mustache' +require 'yaml' + +require_relative '../fastlane/touchlane/lib/touchlane/configuration_type' + +class String + def in_current_dir + "#{__dir__}/#{self}" + end +end + +class ConfigRenderer + class XCConfigKeys + DEVELOPMENT_TEAM = "DEVELOPMENT_TEAM" + PRODUCT_BUNDLE_IDENTIFIER = "PRODUCT_BUNDLE_IDENTIFIER" + CODE_SIGN_STYLE = "CODE_SIGN_STYLE" + end + + INHERITED_PREFIX = "$(inherited)" + + private_constant :INHERITED_PREFIX + + def initialize(configurations_file_path, build_parameters_path, configs_folder_name) + @configurations_file_path = configurations_file_path + @build_parameters_path = build_parameters_path + @configs_folder_name = configs_folder_name + end + + def render_xconfigs + temp_configs_data_file_path = "configs_data.json".in_current_dir + generator_path = "build_options_helper/helper.py".in_current_dir + template_path = "target_xcconfig.mustache".in_current_dir + + # Create config directory if needed + Dir.mkdir(@configs_folder_name) unless Dir.exist?(@configs_folder_name) + + # Call python script and generate configs to config file + system("python #{generator_path} -bp #{@build_parameters_path} -o #{__dir__} -r ios_build_settings -p ios") + + # Open settings, configurations and template files + target_xcconfig_tempate = File.read(template_path) + $configurations = YAML.load(File.open(@configurations_file_path)) + $config_types = $configurations["types"] + + targets = $configurations["targets"] + + # Run through all target in project + targets.each do |target_name, target| + + # Need open everytime, because script make some changes only for this target + configs = JSON.load(File.open(temp_configs_data_file_path)) + + # Run through all configs + configs.each do |config| + + # Take default values + distribution_type = Touchlane::ConfigurationType.from_account_type(config["account_type"]).type + properties = target[distribution_type] + + # Add properties from settings file + properties.each do |key, value| + if config["xcconfig_options"].any? { |option| key == option["key"] } + config["xcconfig_options"].map! { |option| key == option["key"] ? merge_config_data(key, option["value"], value) : option } + else + config["xcconfig_options"].append(config_option(key, value)) + end + end + + # Add missing properties if needed + config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, distribution_type)) + + # Create settings pack + config_data = { + "target_name": target_name, + "abstract_targets_prefix": target["abstract_targets_prefix"], + "configuration": config + } + + # Create file for every setting in loop + File.open(@configs_folder_name + "/" + target_name + config["name"] + ".xcconfig", 'w') { |file| + file.puts(Mustache.render(target_xcconfig_tempate, config_data)) + } + end + + end + + # Remove config file, it's trash + File.delete(temp_configs_data_file_path) if File.exist?(temp_configs_data_file_path) + end + + # Make tuple of key and value become mustache template element + def config_option(key, value) + return { "key" => key, "value" => value } + end + + def merge_config_data(key, config_value, settings_value) + if settings_value.start_with?(INHERITED_PREFIX) + new_value = settings_value.split(INHERITED_PREFIX).last + return config_option(key, config_value + new_value) + else + return config_option(key, settings_value) + end + end + + # Fetch development team from build configuration + def fetch_development_team(development_team_key, distribution_type) + current_config = $config_types[distribution_type] + team_value = current_config["team_id"] + return config_option(development_team_key, team_value) + end + + # Generate missing properties if needed + def generate_missing_properties(target_name, properties, distribution_type) + result = [] + + # Bundle_id_key should be among the properties (required by fastlane) + unless properties.key?(XCConfigKeys::PRODUCT_BUNDLE_IDENTIFIER) + raise "#{target_name}: Could not find #{XCConfigKeys::PRODUCT_BUNDLE_IDENTIFIER} for #{distribution_type}" + end + + unless properties.key?(XCConfigKeys::DEVELOPMENT_TEAM) + result.append(fetch_development_team(XCConfigKeys::DEVELOPMENT_TEAM, distribution_type)) + end + + unless properties.key?(XCConfigKeys::CODE_SIGN_STYLE) + result.append(config_option(XCConfigKeys::CODE_SIGN_STYLE, "Manual")) + end + + return result + end +end \ No newline at end of file diff --git a/xcode/config_generator/example_settings.yaml b/xcode/config_generator/example_settings.yaml index 89d52c3..624b218 100644 --- a/xcode/config_generator/example_settings.yaml +++ b/xcode/config_generator/example_settings.yaml @@ -5,10 +5,12 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: "ru.touchin.testproject" PROVISIONING_PROFILE_SPECIFIER: "TestProjectDev" CODE_SIGN_ENTITLEMENTS: "TestProject/Standard.entitlements" + SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) DEBUG_MENU" enterprise: PRODUCT_BUNDLE_IDENTIFIER: "com.touchin.testproject" PROVISIONING_PROFILE_SPECIFIER: "TestProjectEnterprise" CODE_SIGN_ENTITLEMENTS: "TestProject/Enterprise.entitlements" + SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) DEBUG_MENU" appstore: PRODUCT_BUNDLE_IDENTIFIER: "ru.customer.domain" PROVISIONING_PROFILE_SPECIFIER: "TestProjectAppStore" @@ -16,13 +18,13 @@ targets: types: development: - apple_id: "apple@touchin.ru" + apple_id: "iosdev@touchin.ru" team_id: "**********" itc_team_id: "**********" enterprise: apple_id: "enterpriseapple@touchin.ru" team_id: "**********" appstore: - apple_id: "apple@touchin.ru" + apple_id: "iosdev@touchin.ru" team_id: "**********" itc_team_id: "**********" \ No newline at end of file diff --git a/xcode/config_generator/render_xcconfigs.rb b/xcode/config_generator/render_xcconfigs.rb index 79d32cf..ba20335 100755 --- a/xcode/config_generator/render_xcconfigs.rb +++ b/xcode/config_generator/render_xcconfigs.rb @@ -1,7 +1,4 @@ -require 'json' -require 'mustache' -require 'yaml' - +require_relative "config_renderer" # # Usage: render_xcconfigs.rb [] # @@ -10,122 +7,9 @@ require 'yaml' # It is recommended to remove old .xcconfig files before running this script. # -class String - def in_current_dir - "#{__dir__}/#{self}" - end -end - # Input files paths configurations_file_path = ARGV[0] -temp_configs_data_file_path = "configs_data.json".in_current_dir -generator_path = "build_options_helper/helper.py".in_current_dir -template_path = "target_xcconfig.mustache".in_current_dir build_parameters_path = ARGV[1] configs_folder_name = ARGV[2] || "TargetConfigurations" -# Create config directory if needed -Dir.mkdir(configs_folder_name) unless Dir.exist?(configs_folder_name) - -# Call python script and generate configs to config file -system("python #{generator_path} -bp #{build_parameters_path} -o #{__dir__} -r ios_build_settings -p ios") - -# Open settings, configurations and template files -target_xcconfig_tempate = File.read(template_path) -$configurations = YAML.load(File.open(configurations_file_path)) -$config_types = $configurations["types"] - -# Set global property -targets = $configurations["targets"] - -# Make tuple of key and value become mustache template element -def config_option(key, value) - return { "key" => key, "value" => value } -end - -# Maps lane prefix to distribution type -def distribution_type_of(account_type) - case account_type - when "Standard" - "development" - when "Enterprise" - "enterprise" - when "AppStore" - "appstore" - else - raise "Error: Unsupported distribution type #{account_type}" - end -end - -# Fetch development team from build configuration -def fetch_development_team(development_team_key, distribution_type) - current_config = $config_types[distribution_type] - team_value = current_config["team_id"] - return config_option(development_team_key, team_value) -end - -# Generate missing properties if needed -def generate_missing_properties(target_name, properties, distribution_type) - result = [] - development_team_key = "DEVELOPMENT_TEAM" - bundle_id_key = "PRODUCT_BUNDLE_IDENTIFIER" - code_sign_style_key = "CODE_SIGN_STYLE" - - # Bundle_id_key should be among the properties (required by fastlane) - unless properties.key?(bundle_id_key) - raise "#{target_name}: Could not find #{bundle_id_key} for #{distribution_type}" - end - - unless properties.key?(development_team_key) - result.append(fetch_development_team(development_team_key, distribution_type)) - end - - unless properties.key?(code_sign_style_key) - result.append(config_option(code_sign_style_key, "Manual")) - end - - return result -end - -# Run through all target in project -targets.each do |target_name, target| - - # Need open everytime, because script make some changes only for this target - configs = JSON.load(File.open(temp_configs_data_file_path)) - - # Run through all configs - configs.each do |config| - - # Take default values - distribution_type = distribution_type_of(config["account_type"]) - properties = target[distribution_type] - - # Add properties from settings file - properties.each do |key, value| - if config["xcconfig_options"].any? { |option| key == option["key"] } - config["xcconfig_options"].map! { |option| key == option["key"] ? config_option(key, value) : option } - else - config["xcconfig_options"].append(config_option(key, value)) - end - end - - # Add missing properties if needed - config["xcconfig_options"].concat(generate_missing_properties(target_name, properties, distribution_type)) - - # Create settings pack - config_data = { - "target_name": target_name, - "abstract_targets_prefix": target["abstract_targets_prefix"], - "configuration": config - } - - # Create file for every setting in loop - File.open(configs_folder_name + "/" + target_name + config["name"] + ".xcconfig", 'w') { |file| - file.puts(Mustache.render(target_xcconfig_tempate, config_data)) - } - end - -end - -# Remove config file, it's trash -File.delete(temp_configs_data_file_path) if File.exist?(temp_configs_data_file_path) +ConfigRenderer.new(configurations_file_path, build_parameters_path, configs_folder_name).render_xconfigs() diff --git a/xcode/fastlane/touchlane/lib/touchlane/configuration_type.rb b/xcode/fastlane/touchlane/lib/touchlane/configuration_type.rb index 96f5412..9c9e455 100644 --- a/xcode/fastlane/touchlane/lib/touchlane/configuration_type.rb +++ b/xcode/fastlane/touchlane/lib/touchlane/configuration_type.rb @@ -57,6 +57,20 @@ module Touchlane new(type) end + def self.from_account_type(account_type) + case account_type + when DEVELOPMENT_PREFIX + from_type(DEVELOPMENT) + when ENTERPRISE_PREFIX + from_type(ENTERPRISE) + when APP_STORE_PREFIX + from_type(APP_STORE) + else + raise "Unable to map #{account_type} to #{ConfigurationType.class}." + + "Available account types: #{DEVELOPMENT_PREFIX}, #{ENTERPRISE_PREFIX}, #{APP_STORE_PREFIX}" + end + end + def to_options { :type => @type,