# frozen_string_literal: true require 'set' require 'jazzy/symbol_graph/graph' require 'jazzy/symbol_graph/constraint' require 'jazzy/symbol_graph/symbol' require 'jazzy/symbol_graph/relationship' require 'jazzy/symbol_graph/sym_node' require 'jazzy/symbol_graph/ext_node' require 'jazzy/symbol_graph/ext_key' # This is the top-level symbolgraph driver that deals with # figuring out arguments, running the tool, and loading the # results. module Jazzy module SymbolGraph # Find swift symbol graph files, either having been passed # in directly, or generated by running`swift symbolgraph-extract` # with configured args. # Then parse the results, and return as JSON in SourceKit[ten] # format. def self.build(module_config) if module_config.symbolgraph_directory.nil? Dir.mktmpdir do |tmp_dir| args = arguments(module_config, tmp_dir) Executable.execute_command('swift', args.unshift('symbolgraph-extract'), true) # raise on error parse_symbols(tmp_dir) end else parse_symbols(module_config.symbolgraph_directory.to_s) end end # Figure out the args to pass to symbolgraph-extract def self.arguments(module_config, output_path) if module_config.module_name.empty? raise 'error: `--swift-build-tool symbolgraph` requires `--module`.' end user_args = module_config.build_tool_arguments.join if user_args =~ /-(?:module-name|minimum-access-level|output-dir)/ raise 'error: `--build-tool-arguments` for ' \ "`--swift-build-tool symbolgraph` can't use `-module`, " \ '`-minimum-access-level`, or `-output-dir`.' end # Default set args = [ '-module-name', module_config.module_name, '-minimum-access-level', 'private', '-output-dir', output_path, '-skip-synthesized-members' ] # Things user can override args += ['-sdk', sdk(module_config)] unless user_args =~ /-sdk/ args += ['-target', target] unless user_args =~ /-target/ args += ['-F', module_config.source_directory.to_s] unless user_args =~ /-F(?!s)/ args += ['-I', module_config.source_directory.to_s] unless user_args =~ /-I/ args + module_config.build_tool_arguments end # Parse the symbol files in the given directory def self.parse_symbols(directory) Dir[directory + '/*.symbols.json'].sort.map do |filename| # The @ part is for extensions in our module (before the @) # of types in another module (after the @). File.basename(filename) =~ /(.*?)(@(.*?))?\.symbols/ module_name = Regexp.last_match[1] ext_module_name = Regexp.last_match[3] || module_name json = File.read(filename) { filename => Graph.new(json, module_name, ext_module_name).to_sourcekit, } end.to_json end # Get the SDK path. On !darwin this just isn't needed. def self.sdk(module_config) `xcrun --show-sdk-path --sdk #{module_config.sdk}`.chomp end # Guess a default LLVM target. Feels like the tool should figure this # out from sdk + the binary somehow? def self.target `swift -version` =~ /Target: (.*?)$/ Regexp.last_match[1] || 'x86_64-apple-macosx10.15' end # This is a last-ditch fallback for when symbolgraph doesn't # provide a name - at least conforming external types to local # protocols. def self.demangle(usr) args = %w[demangle -simplified -compact].append(usr.sub(/^s:/, 's')) output, = Executable.execute_command('swift', args, true) output.chomp rescue StandardError usr end end end