#!/usr/bin/env ruby # # Copyright (c) 2017 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. require "fileutils" require 'erb' require 'optparse' require 'yaml' options = { :input => nil, :outputDirectory => nil } optparse = OptionParser.new do |opts| opts.banner = "Usage: #{File.basename($0)} --input file" opts.separator "" opts.on("--input input", "file to generate settings from") { |input| options[:input] = input } opts.on("--outputDir output", "directory to generate file in") { |output| options[:outputDirectory] = output } end optparse.parse! if !options[:input] puts optparse exit -1 end if !options[:outputDirectory] options[:outputDirectory] = Dir.getwd end FileUtils.mkdir_p(options[:outputDirectory]) parsedSettings = begin YAML.load_file(options[:input]) rescue ArgumentError => e puts "Could not parse input file: #{e.message}" exit(-1) end class Setting attr_accessor :name attr_accessor :type attr_accessor :initial attr_accessor :conditional attr_accessor :onChange attr_accessor :getter def initialize(name, opts) @name = name @type = opts["type"] || "bool" @initial = opts["initial"] @conditional = opts["conditional"] @onChange = opts["onChange"] @getter = opts["getter"] end def valueType? @type != "String" && @type != "URL" end def idlType # FIXME: Add support for more types including enumerate types. if @type == "int" "long" elsif @type == "unsigned" "unsigned long" elsif @type == "double" "double" elsif @type == "float" "float" elsif @type == "String" "DOMString" elsif @type == "bool" "boolean" else nil end end def parameterType if valueType? @type else "const #{@type}&" end end def hasComplexSetter? @onChange != nil end def setterFunctionName if @name.start_with?("css", "xss", "ftp", "dom", "dns") "set" + @name[0..2].upcase + @name[3..@name.length] else "set" + @name[0].upcase + @name[1..@name.length] end end def getterFunctionName @getter || @name end end class Conditional attr_accessor :condition attr_accessor :settings attr_accessor :boolSettings attr_accessor :nonBoolSettings attr_accessor :settingsWithComplexSetters def initialize(condition, settings) @condition = condition @settings = settings @boolSettings = @settings.select { |setting| setting.type == "bool" } @nonBoolSettings = @settings.reject { |setting| setting.type == "bool" } @settingsWithComplexSetters = @settings.select { |setting| setting.hasComplexSetter? } end end class Settings attr_accessor :settings attr_accessor :unconditionalSetting attr_accessor :unconditionalBoolSetting attr_accessor :unconditionalNonBoolSetting attr_accessor :unconditionalSettingWithComplexSetters attr_accessor :conditionals def initialize(hash) @settings = [] hash.each do |name, options| @settings << Setting.new(name, options) end @settings.sort! { |x, y| x.name <=> y.name } @unconditionalSetting = @settings.reject { |setting| setting.conditional } @unconditionalBoolSetting = @unconditionalSetting.select { |setting| setting.type == "bool" } @unconditionalNonBoolSetting = @unconditionalSetting.reject { |setting| setting.type == "bool" } @unconditionalSettingWithComplexSetters = @unconditionalSetting.select { |setting| setting.hasComplexSetter? } @conditionals = [] conditionalsMap = {} @settings.select { |setting| setting.conditional }.each do |setting| if !conditionalsMap[setting.conditional] conditionalsMap[setting.conditional] = [] end conditionalsMap[setting.conditional] << setting end conditionalsMap.each do |key, value| @conditionals << Conditional.new(key, value) end @conditionals.sort! { |x, y| x.condition <=> y.condition } end def renderToFile(template, file) template = File.join(File.dirname(__FILE__), template) output = ERB.new(File.read(template), 0, "-").result(binding) File.open(file, "w+") do |f| f.write(output) end end end settings = Settings.new(parsedSettings) settings.renderToFile("SettingsTemplates/Settings.h.erb", File.join(options[:outputDirectory], "Settings.h")) settings.renderToFile("SettingsTemplates/Settings.cpp.erb", File.join(options[:outputDirectory], "Settings.cpp")) settings.renderToFile("SettingsTemplates/InternalSettingsGenerated.idl.erb", File.join(options[:outputDirectory], "InternalSettingsGenerated.idl")) settings.renderToFile("SettingsTemplates/InternalSettingsGenerated.h.erb", File.join(options[:outputDirectory], "InternalSettingsGenerated.h")) settings.renderToFile("SettingsTemplates/InternalSettingsGenerated.cpp.erb", File.join(options[:outputDirectory], "InternalSettingsGenerated.cpp"))