class VisualStudio::Product

Constants

NAMES
NAME_TO_PRETTY_NAME
PRETTY_NAMES

Attributes

binaries[R]
includes[R]
libraries[R]
name[R]
root[R]
sdks[R]
shared[R]
supports[R]
version[R]

Public Class Methods

find(product, version) click to toggle source
# File lib/visual_studio/product.rb, line 99
def self.find(product, version)
  if VisualStudio::Product::NAMES.include?(product)
    name = Helpers::PrettyString.new product,
                                     pretty: VisualStudio::VERSION_TO_PRETTY_NAME[version]

    root = self._find_via_registry(product, version)
    return nil if root.nil?

    # If shared, indicates everything is fucked up and Microsoft can't be
    # trusted. As such, we need to unfuck things downstream.
    shared = root.downcase.include? 'shared'

    includes, libraries, binaries =
      case product
        when 'VC'
          case version.to_f
            when 8.0..11.0
              # TODO(mtwilliams): Check if x86_64 support exists.
              includes  = [File.join(root, 'include')]
              libraries = {:x86    => [File.join(root, 'lib')],
                           :x86_64 => []}
              binaries  = {:x86    => [File.join(root, 'bin')],
                           :x86_64 => []}
              [includes, libraries, binaries]
            when 12.0..14.0
              # TODO(mtwilliams): Select the 64-bit and ARM host variants
              # when applicable, i.e. when running on 64-bit or ARM.
              includes  = [File.join(root, 'include')]
              libraries = {:x86    => [File.join(root, 'lib')],
                           :x86_64 => [File.join(root, 'lib', 'amd64')],
                           :arm    => [File.join(root, 'lib', 'arm')]}
              binaries  = {:x86    => [File.join(root, 'bin')],
                           :x86_64 => [File.join(root, 'bin', 'x86_amd64')],
                           :arm    => [File.join(root, 'bin', 'x86_arm')]}
              [includes, libraries, binaries]
            else
              # TODO(mtwilliams): Raise a proper extension.
              # raise VisualStudio::Unsupported(...)
              raise "Wha-?"
            end
        when 'VC#'
          # TODO(mtwilliams): Determine search paths.
          [[], {}, {}]
        end

    # TODO(mtwilliams): Handle the Xbox One and PS4.
    sdks = (VisualStudio::VERSION_TO_SDKS[version][:windows].map do |version|
              Windows::SoftwareDevelopmentKit.find(version)
            end).compact

    platforms = [:windows]
    architectures = case product
                      when 'VC'
                        case version.to_f
                          when 8.0..11.0
                            # TODO(mtwilliams): Check if x86_64 support exists.
                            [:x86]
                          when 12.0..14.0
                            [:x86, :x86_64, :arm]
                          end
                      when 'VC#'
                        # TODO(mtwilliams): Determine 64-bit support?
                        [:x86, :x86_64, :arm]
                      end

    VisualStudio::Product.new(name: name,
                              version: version,
                              root: root,
                              includes: includes,
                              libraries: libraries,
                              binaries: binaries,
                              sdks: ({windows: sdks}),
                              supports: ({platforms: platforms,
                                          architectures: architectures}),
                              shared: shared)
  else
    # TODO(mtwilliams): Raise an exception.
    # raise VisualStudio::InvalidVersion.new(...)
  end
end
new(desc) click to toggle source
# File lib/visual_studio/product.rb, line 17
def initialize(desc)
  @name      = desc[:name]
  @version   = desc[:version]
  @root      = desc[:root]
  @includes  = desc[:includes]
  @libraries = desc[:libraries]
  @binaries  = desc[:binaries]
  @sdks      = desc[:sdks]
  @supports  = desc[:supports]
  @shared    = desc[:shared]
end

Private Class Methods

_find_via_registry(product, version) click to toggle source
# File lib/visual_studio/product.rb, line 181
def self._find_via_registry(product, version)
  case version.to_f
    when 8.0..14.0
      # We try to find a full version of Visual Studio. If we can't, then
      # we look for standalone verions, i.e. Express Editions. This is only
      # required for 2005-2010 so this logic can be removed when we drop
      # support for them.
      keys = ["SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\#{version}\\Setup\\#{product}",
              "SOFTWARE\\Microsoft\\VisualStudio\\#{version}\\Setup\\#{product}",
              "SOFTWARE\\Wow6432Node\\Microsoft\\VCExpress\\#{version}\\Setup\\#{product}",
              "SOFTWARE\\Microsoft\\VCExpress\\#{version}\\Setup\\#{product}"]
      installs = keys.map do |key|
        begin
          require 'win32/registry'
          return File.expand_path(::Win32::Registry::HKEY_LOCAL_MACHINE.open(key, ::Win32::Registry::KEY_READ)['ProductDir']).to_s
        rescue
        end
      end
      installs.compact.first
    when 15.0
      raise "Not supported yet!"
    end        
end

Public Instance Methods

environment(opts={}) click to toggle source
# File lib/visual_studio/product.rb, line 29
def environment(opts={})
  # TODO(mtwilliams): Raise an exception.
  return nil unless @name.to_s == 'VC'

  target = opts[:target] || {platform: :windows,
                             architecture: :x86}

  # TODO(mtwilliams): Handle other platforms.
  # TODO(mtwilliams): Check if the architecture is supported.
  platform = :windows
  arch = {:x86 => 'x86', :x86_64 => 'amd64', :arm => 'arm'}[target[:architecture]]

  # TODO(mtwilliams): Raise an exception.
  return nil unless arch

  if @shared
    # HACK(mtwilliams): Microsoft shipped a broken `vcvarsall.bat`, so
    # we need to build the envrionment ourself.

    root = File.expand_path(File.join(@root, ".."))

    # TODO(mtwilliams): Insert missing variables into environment.
     # WindowsSdkDir
     # WindowsLibPath
     # WindowsSDKVersion
     # UCRTVersion
     # UniversalCRTSdkDir
     # DevEnvDir
     # INCLUDE
     # LIB
     # LIBPATH

    path = []

    # TODO(mtwilliams): Inject latest Windows SDK into PATH.
    case arch
      when 'x86'
        path << File.join(@root, "bin")
      else
        path << File.join(@root, "bin", arch)
      end

    env = {
      "VS140COMNTOOLS" => File.join(root, "Common7", "Tools"),
      "VSINSTALLDIR"   => root,
      "VCINSTALLDIR"   => File.join(root, "VC"),
      "PATH"           => path.join(';')
    }

    env = VisualStudio::Environment.merge(opts[:base] || {}, env)
    env = VisualStudio::Environment.merge(env, opts[:overlay] || {})

    env
  else
    # HACK(mtwilliams): We should reimplement this logic inside this gem.
    require 'open3'
    require 'json'

    cmd   = "call \"#{File.join(@root, 'vcvarsall.bat')}\" #{arch} & " +
            "echo require('json'); print JSON.generate(ENV.to_h); | ruby\n"
    out, _, status = Open3.capture3(ENV.to_h, "cmd.exe /C \"#{cmd}\"")
    return nil unless status == 0

    env = VisualStudio::Environment.merge(opts[:base] || {}, JSON.parse(out))
    env = VisualStudio::Environment.merge(env, opts[:overlay] || {})

    env
  end
end