module Swift::Gist

Constants

ERB_TEMPLATE
VERSION

Public Class Methods

format_swift_module(swift_module) click to toggle source

This function generates a valid swift case for each SwiftModule.

The resulting output will be something like:

.target(name: “SomeModule”) .target(name: “SomeModule”, dependencies: [“SomeOtherModule”]) .testTarget(name: “SomeModuleTests”, dependencies: [“SomeOtherModule”])

# File lib/swift/gist/format_swift_module.rb, line 11
def self.format_swift_module swift_module
    target_type = swift_module.type == :src ? 'target' : 'testTarget'
    formatted_name = %Q|name: "#{swift_module.name}"|
    formatted_dependencies = "dependencies: [%s]" % swift_module.depends_on.map { |dependency| %Q|"#{dependency}"| }.join(', ')

    %Q|.%{target_type}(%{formatted_name}%{formatted_dependencies})| % {
      target_type: target_type,
      formatted_name: formatted_name,
      formatted_dependencies: swift_module.depends_on.count > 0 ? ", #{formatted_dependencies}" : ""
    }
end
generate_project( swift_modules, mktmpdir: Dir.method(:mktmpdir), open: File.method(:open), spm_package_creator: method(:spm_package_definition_from_swift_modules), spm_project_creator: method(:spm_project_from_swift_modules) ) click to toggle source
# File lib/swift/gist/generate_project.rb, line 4
def self.generate_project(
  swift_modules,
  mktmpdir: Dir.method(:mktmpdir),
  open: File.method(:open),
  spm_package_creator: method(:spm_package_definition_from_swift_modules),
  spm_project_creator: method(:spm_project_from_swift_modules)
)

  mktmpdir.call.tap do |tmp_dir|
    spm_project_creator.call swift_modules, tmp_dir: tmp_dir

    open.call("#{tmp_dir}/Package.swift", 'w') do |file|
      file.puts spm_package_creator.call(swift_modules)
    end
  end

end
generate_project_art(swift_modules, tmp_dir) click to toggle source
# File lib/swift/gist/generate_project_art.rb, line 3
def self.generate_project_art swift_modules, tmp_dir
  description = StringIO.new
  description.puts tmp_dir
  description.puts '.'
  description.puts "├── Package.swift"
  description.puts "└── Sources"
  swift_modules.sort_by { |swift_module| swift_module.name }.each_with_index do |swift_module, index|
    description.print index == swift_modules.length - 1 ? "    └── " : "    ├── "
    description.puts swift_module.name

    swift_module.sources.sort.each_with_index do |source, source_index|
      description.print index == swift_modules.length - 1               ? "     " : "    │"
      description.print source_index == swift_module.sources.length - 1 ? "   └── " : "   ├── "
      description.puts source
    end
  end
  description.string
end
parse_command_line_arguments(arguments) click to toggle source

The CLI creates a new module for every `–module` or `–test-module` argument. The `–source` and `–depends-on` arguments are all applied to the last defined swift module.

# File lib/swift/gist/cli.rb, line 16
    def self.parse_command_line_arguments arguments
      unless arguments.count > 0
        error = CLIError.new(<<REASON
Usage: swift-gist [--module[=<name>]] [--test-module[=<name>]]
                  [--source[=<glob>]] [--depends-on[=<module name>]]
REASON
)
        raise error
      end

      swift_modules = []

      OptionParser.new do |parser|
        parser.on('--module MODULE') do |module_name|
          swift_modules << SwiftModule.new(module_name, :src, [])
        end

        parser.on('--source SOURCE') do |source|
          if swift_modules.last.nil?
            raise CLIError.new("Error: The `--source` argument requires that a `--module` or `--test-module` has already been defined.")
          end

          swift_modules.last.sources |= Dir[source]
        end

        parser.on('--test-module MODULE') do |module_name|
          swift_modules << SwiftModule.new(module_name, :test, [])
        end

        parser.on('--depends-on MODULE') do |module_name|
          if swift_modules.last.nil?
            raise CLIError.new("Error: The `--depends-on` argument requires that a `--module` or `--test-module` has already been defined.")
          end
          swift_modules.last.depends_on << module_name
        end
      end.parse arguments

      swift_modules.each do |swift_module|
        if swift_module.sources.count == 0
          raise CLIError.new("Error: The module '#{swift_module.name}' does not have any valid sources.")
        end
      end

      swift_modules
    end
run( arguments, chdir: Dir.method(:chdir), cli: method(:parse_command_line_arguments), formatted_date: -> { Time.iso8601(Time.now.iso8601) } click to toggle source

This is the ugly command that binds everything together.

1 - Parses command line arguments 2 - Creates a SPM project linking just the files specified with `–source` arguments

# File lib/swift/gist/runner.rb, line 12
def self.run(
  arguments,
  chdir: Dir.method(:chdir),
  cli: method(:parse_command_line_arguments),
  formatted_date: -> { Time.iso8601(Time.now.iso8601) },
  project_art_generator: method(:generate_project_art),
  project_generator: method(:generate_project),
  rm_rf: FileUtils.method(:rm_rf),
  stdin_watcher: method(:watch_stdin),
  stdout: $stdout.method(:puts),
  system: method(:system),
  watcher: method(:watch_sources)
)

  begin
    swift_modules = cli.call arguments
  rescue CLIError => error
    puts error.reason
    exit
  end

  begin
    tmp_dir = project_generator.call(swift_modules)
    stdout.call project_art_generator.call(swift_modules, tmp_dir)

    chdir.call(tmp_dir) do
      system.call('swift test')
    end

    counter = 1

    build_command = -> {
      stdout.call "\n\n-----> Running `$ swift test` @ '#{formatted_date.call}' - Build ##{counter}"
      chdir.call(tmp_dir) do
        system.call('swift test')
      end

      counter += 1
    }

    watcher.call(swift_modules, &build_command)
    stdin_watcher.call(&build_command)

  ensure
    rm_rf.call tmp_dir
  end

  0
end
spm_package_definition_from_swift_modules(swift_modules, format_swift_module: method(:format_swift_module)) click to toggle source
# File lib/swift/gist/spm_package_creator.rb, line 5
def self.spm_package_definition_from_swift_modules swift_modules, format_swift_module: method(:format_swift_module)
  formatted_modules = swift_modules.map { |swift_module| format_swift_module.call swift_module }
  ERB.new(ERB_TEMPLATE).result(binding)
end
spm_project_from_swift_modules( swift_modules, tmp_dir:, mkdir_p: FileUtils.method(:mkdir_p), pwd: Dir.method(:pwd), ln_s: FileUtils.method(:ln_s) ) click to toggle source
# File lib/swift/gist/spm_project_creator.rb, line 4
def self.spm_project_from_swift_modules(
  swift_modules,
  tmp_dir:,
  mkdir_p: FileUtils.method(:mkdir_p),
  pwd: Dir.method(:pwd),
  ln_s: FileUtils.method(:ln_s)
)

  swift_modules.each do |swift_module|
    mkdir_p.call "#{tmp_dir}/Sources/#{swift_module.name}"
    swift_module.sources.each do |source|
      ln_s.call "#{pwd.call}/#{source}", "#{tmp_dir}/Sources/#{swift_module.name}"
    end
  end
end
watch_sources(swift_modules, listener: Listen) { || ... } click to toggle source
# File lib/swift/gist/file_watcher.rb, line 5
def self.watch_sources swift_modules, listener: Listen
  sources = Set.new(swift_modules.map { |swift_module| swift_module.sources }.flatten)
  dirs    = Set.new(sources.map { |path| Dir.exist?(path) ? path : File.dirname(path) })

  listener.to(*dirs, only: /.*swift/, relative: true) do |modified, created, deleted|
    next unless sources.intersect?(Set.new(modified + created + deleted))
    yield
  end.start
end
watch_stdin(stdin: STDIN) { || ... } click to toggle source
# File lib/swift/gist/stdin_watcher.rb, line 4
def self.watch_stdin stdin: STDIN
  while stdin.gets
    yield
  end
end