Class: Omnibus::Packager::MSI

Inherits:
WindowsBase show all
Defined in:
lib/omnibus/packagers/msi.rb

Constant Summary

Constants inherited from WindowsBase

WindowsBase::DEFAULT_TIMESTAMP_SERVERS

Constants included from Util

Util::SHELLOUT_OPTIONS

Constants included from NullArgumentable

NullArgumentable::NULL

Instance Attribute Summary

Attributes inherited from Base

#project

DSL methods collapse

Instance Method Summary collapse

Methods inherited from WindowsBase

#algorithm, #cert_store_name, #certificate_subject, #machine_store?, #sign_package, #signing_identity, #thumbprint, #timestamp_servers, #try_sign, #windows_package_version

Methods inherited from Base

build, #exclusions, id, #id, #initialize, #install_dir, #package_path, #resource_path, #resources_path, #run!, setup, #skip_packager, #staging_dir, #staging_dir_path

Methods included from Util

#compiler_safe_path, #copy_file, #create_directory, #create_file, #create_link, included, #path_key, #remove_directory, #remove_file, #retry_block, #shellout, #shellout!, #windows_safe_path

Methods included from Templating

included, #render_template, #render_template_content

Methods included from Sugarable

extended, included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Instrumentation

#measure

Methods included from Digestable

#digest, #digest_directory, included

Constructor Details

This class inherits a constructor from Omnibus::Packager::Base

Instance Method Details

#bundle_msi(val = false) ⇒ TrueClass, FalseClass

Signal that we’re building a bundle rather than a single package

Examples:

bundle_msi true

Parameters:

  • value (TrueClass, FalseClass)

    whether we’re a bundle or not

Returns:

  • (TrueClass, FalseClass)

    whether we’re a bundle or not



226
227
228
229
230
231
232
# File 'lib/omnibus/packagers/msi.rb', line 226

def bundle_msi(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:bundle_msi, "be TrueClass or FalseClass")
  end

  @bundle_msi ||= val
end

#bundle_nameObject



323
324
325
# File 'lib/omnibus/packagers/msi.rb', line 323

def bundle_name
  "#{project.package_name}-#{project.build_version}-#{project.build_iteration}-#{Config.windows_arch}.exe"
end

#candle_command(is_bundle: false) ⇒ String

Get the shell command to complie the project WIX files

Returns:

  • (String)


482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
# File 'lib/omnibus/packagers/msi.rb', line 482

def candle_command(is_bundle: false)
  if is_bundle
    <<-EOH.split.join(" ").squeeze(" ").strip
    candle.exe
      -nologo
      #{wix_candle_flags}
      -ext WixBalExtension
      #{wix_extension_switches(wix_candle_extensions)}
      -dOmnibusCacheDir="#{windows_safe_path(File.expand_path(Config.cache_dir))}"
      "#{windows_safe_path(staging_dir, "bundle.wxs")}"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      candle.exe
        -nologo
        #{wix_candle_flags}
        #{wix_extension_switches(wix_candle_extensions)}
        -dProjectSourceDir="#{windows_safe_path(project.install_dir)}" "project-files.wxs"
        "#{windows_safe_path(staging_dir, "source.wxs")}"
    EOH
  end
end

#fast_msi(val = false) ⇒ TrueClass, FalseClass

Signal that we’re building a zip-based MSI

Examples:

fast_msi true

Parameters:

  • value (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not

Returns:

  • (TrueClass, FalseClass)

    whether we’re building a zip-based MSI or not



246
247
248
249
250
251
252
# File 'lib/omnibus/packagers/msi.rb', line 246

def fast_msi(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:fast_msi, "be TrueClass or FalseClass")
  end

  @fast_msi ||= val
end

#gem_path(glob = NULL) ⇒ String

Discovers a path to a gem/file included in a gem under the install directory.

Raises exception the glob matches 0 or more than 1 file/directory.

Examples:

gem_path 'chef-[0-9]*-mingw32' -> 'some/path/to/gems/chef-version-mingw32'

Parameters:

  • glob (String) (defaults to: NULL)

    a ruby acceptable glob path such as with **, *, [] etc.

Returns:

  • (String)

    path relative to the project’s install_dir



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/omnibus/packagers/msi.rb', line 291

def gem_path(glob = NULL)
  unless glob.is_a?(String) || null?(glob)
    raise InvalidValue.new(:glob, "be an String")
  end

  install_path = Pathname.new(project.install_dir)

  # Find path in which the Chef gem is installed
  search_pattern = install_path.join("**", "gems")
  search_pattern = search_pattern.join(glob) unless null?(glob)
  file_paths = Pathname.glob(search_pattern).find

  raise "Could not find `#{search_pattern}'!" if file_paths.none?
  raise "Multiple possible matches of `#{search_pattern}'! : #{file_paths}" if file_paths.count > 1

  file_paths.first.relative_path_from(install_path).to_s
end

#heat_commandString

Get the shell command to run heat in order to create a a WIX manifest of project files to be packaged into the MSI

Returns:

  • (String)


457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/omnibus/packagers/msi.rb', line 457

def heat_command
  if fast_msi
    <<-EOH.split.join(" ").squeeze(" ").strip
      heat.exe file "#{project.name}.zip"
      -cg ProjectDir
      -dr INSTALLLOCATION
      -nologo -sfrag -srd -sreg -gg
      -out "project-files.wxs"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      heat.exe dir "#{windows_safe_path(project.install_dir)}"
        -nologo -srd -sreg -gg -cg ProjectDir
        -dr PROJECTLOCATION
        -var "var.ProjectSourceDir"
        -out "project-files.wxs"
    EOH
  end
end

#light_command(out_file, is_bundle: false) ⇒ String

Get the shell command to link the project WIX object files

Returns:

  • (String)


510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
# File 'lib/omnibus/packagers/msi.rb', line 510

def light_command(out_file, is_bundle: false)
  if is_bundle
    <<-EOH.split.join(" ").squeeze(" ").strip
    light.exe
      -nologo
      #{wix_light_delay_validation}
      -ext WixUIExtension
      -ext WixBalExtension
      #{wix_extension_switches(wix_light_extensions)}
      -cultures:#{localization}
      -loc "#{windows_safe_path(staging_dir, "localization-#{localization}.wxl")}"
      bundle.wixobj
      -out "#{out_file}"
    EOH
  else
    <<-EOH.split.join(" ").squeeze(" ").strip
      light.exe
        -nologo
        #{wix_light_delay_validation}
        -ext WixUIExtension
        #{wix_extension_switches(wix_light_extensions)}
        -cultures:#{localization}
        -loc "#{windows_safe_path(staging_dir, "localization-#{localization}.wxl")}"
        project-files.wixobj source.wixobj
        -out "#{out_file}"
    EOH
  end
end

#localization(val = "en-us") ⇒ String

Set or retrieve the localization. Take a look at this list of valid localizations.

Examples:

localization 'de-de'

Parameters:

  • val (String) (defaults to: "en-us")

    the localization to set

Returns:

  • (String)

    the set localization



269
270
271
272
273
274
275
# File 'lib/omnibus/packagers/msi.rb', line 269

def localization(val = "en-us")
  unless val.is_a?(String)
    raise InvalidValue.new(:localization, "be a String")
  end

  @localization ||= val
end

#msi_display_versionString

The display version calculated from the Omnibus::Project#build_version.

Returns:

  • (String)

See Also:



546
547
548
549
# File 'lib/omnibus/packagers/msi.rb', line 546

def msi_display_version
  versions = project.build_version.split(/[.+-]/)
  "#{versions[0]}.#{versions[1]}.#{versions[2]}"
end

#msi_nameObject



319
320
321
# File 'lib/omnibus/packagers/msi.rb', line 319

def msi_name
  "#{project.package_name}-#{project.build_version}-#{project.build_iteration}-#{Config.windows_arch}.msi"
end

#package_nameObject

See Also:



315
316
317
# File 'lib/omnibus/packagers/msi.rb', line 315

def package_name
  bundle_msi ? bundle_name : msi_name
end

#parameters(val = NULL) ⇒ Hash

Set or retrieve the custom msi building parameters.

Examples:

parameters {
  'MagicParam' => 'ABCD-1234'
}

Parameters:

  • val (Hash) (defaults to: NULL)

    the parameters to set

Returns:

  • (Hash)

    the set parameters



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/omnibus/packagers/msi.rb', line 135

def parameters(val = NULL)
  if null?(val)
    @parameters || {}
  else
    unless val.is_a?(Hash)
      raise InvalidValue.new(:parameters, "be a Hash")
    end

    @parameters = val
  end
end

#resources_dirString

The path where the MSI resources will live.

Returns:

  • (String)


332
333
334
# File 'lib/omnibus/packagers/msi.rb', line 332

def resources_dir
  File.expand_path("#{staging_dir}/Resources")
end

#upgrade_code(val = NULL) ⇒ Hash

Set or retrieve the upgrade code.

Examples:

upgrade_code 'ABCD-1234'

Parameters:

  • val (Hash) (defaults to: NULL)

    the UpgradeCode to set

Returns:

  • (Hash)

    the set UpgradeCode



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/omnibus/packagers/msi.rb', line 108

def upgrade_code(val = NULL)
  if null?(val)
    @upgrade_code || raise(MissingRequiredAttribute.new(self, :upgrade_code, "2CD7259C-776D-4DDB-A4C8-6E544E580AA1"))
  else
    unless val.is_a?(String)
      raise InvalidValue.new(:upgrade_code, "be a String")
    end

    @upgrade_code = val
  end
end

#wix_candle_extension(extension) ⇒ Array

Set the wix candle extensions to load

Examples:

wix_candle_extension 'WixUtilExtension'

Parameters:

  • extension (String)

    A list of extensions to load

Returns:

  • (Array)

    The list of extensions that will be loaded



206
207
208
209
210
211
212
# File 'lib/omnibus/packagers/msi.rb', line 206

def wix_candle_extension(extension)
  unless extension.is_a?(String)
    raise InvalidValue.new(:wix_candle_extension, "be an String")
  end

  wix_candle_extensions << extension
end

#wix_candle_extensionsArray

Returns the extensions to use for candle

Returns:

  • (Array)

    the extensions that will be loaded for candle



567
568
569
# File 'lib/omnibus/packagers/msi.rb', line 567

def wix_candle_extensions
  @wix_candle_extensions ||= []
end

#wix_candle_flagsArray

Returns the options to use for candle

Returns:

  • (Array)

    the extensions that will be loaded for candle



577
578
579
580
# File 'lib/omnibus/packagers/msi.rb', line 577

def wix_candle_flags
  # we support x86 or x64.  No Itanium support (ia64).
  @wix_candle_flags ||= "-arch " + (Config.windows_arch.to_sym == :x86 ? "x86" : "x64")
end

#wix_extension_switches(arr) ⇒ String

Takes an array of wix extension names and creates a string that can be passed to wix to load those.

for example,

‘a’, ‘b’

> “-ext ‘a’ -ext ‘b’”

Returns:

  • (String)


591
592
593
# File 'lib/omnibus/packagers/msi.rb', line 591

def wix_extension_switches(arr)
  "#{arr.map { |e| "-ext '#{e}'" }.join(" ")}"
end

#wix_light_delay_validation(val = false) ⇒ String

Signal delay validation for wix light

Examples:

wix_light_deplay_validation true

Parameters:

  • value (TrueClass, FalseClass)

    whether to delay validation or not

Returns:

  • (String)

    whether we’re a bundle or not



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/omnibus/packagers/msi.rb', line 180

def wix_light_delay_validation(val = false)
  unless val.is_a?(TrueClass) || val.is_a?(FalseClass)
    raise InvalidValue.new(:iwix_light_delay_validation, "be TrueClass or FalseClass")
  end

  @delay_validation ||= val
  unless @delay_validation
    return ""
  end

  "-sval"
end

#wix_light_extension(extension) ⇒ Array

Set the wix light extensions to load

Examples:

wix_light_extension 'WixUtilExtension'

Parameters:

  • extension (String)

    A list of extensions to load

Returns:

  • (Array)

    The list of extensions that will be loaded



160
161
162
163
164
165
166
# File 'lib/omnibus/packagers/msi.rb', line 160

def wix_light_extension(extension)
  unless extension.is_a?(String)
    raise InvalidValue.new(:wix_light_extension, "be an String")
  end

  wix_light_extensions << extension
end

#wix_light_extensionsArray

Returns the extensions to use for light

Returns:

  • (Array)

    the extensions that will be loaded for light



557
558
559
# File 'lib/omnibus/packagers/msi.rb', line 557

def wix_light_extensions
  @wix_light_extensions ||= []
end

#write_bundle_filevoid

This method returns an undefined value.

Write the bundle file into the staging directory.



422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/omnibus/packagers/msi.rb', line 422

def write_bundle_file
  render_template(resource_path("bundle.wxs.erb"),
    destination: "#{staging_dir}/bundle.wxs",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      upgrade_code: upgrade_code,
      parameters: parameters,
      version: windows_package_version,
      display_version: msi_display_version,
      msi: windows_safe_path(Config.package_dir, msi_name),
    })
end

#write_localization_filevoid

This method returns an undefined value.

Write the localization file into the staging directory.



341
342
343
344
345
346
347
348
349
# File 'lib/omnibus/packagers/msi.rb', line 341

def write_localization_file
  render_template(resource_path("localization-#{localization}.wxl.erb"),
    destination: "#{staging_dir}/localization-#{localization}.wxl",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
    })
end

#write_parameters_filevoid

This method returns an undefined value.

Write the parameters file into the staging directory.



356
357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/omnibus/packagers/msi.rb', line 356

def write_parameters_file
  render_template(resource_path("parameters.wxi.erb"),
    destination: "#{staging_dir}/parameters.wxi",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      upgrade_code: upgrade_code,
      parameters: parameters,
      version: windows_package_version,
      display_version: msi_display_version,
    })
end

#write_source_filevoid

This method returns an undefined value.

Write the source file into the staging directory.



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/omnibus/packagers/msi.rb', line 375

def write_source_file
  paths = []

  # Remove C:/
  install_dir = project.install_dir.split("/")[1..-1].join("/")

  # Grab all parent paths
  Pathname.new(install_dir).ascend do |path|
    paths << path.to_s
  end

  # Create the hierarchy
  hierarchy = paths.reverse.inject({}) do |hash, path|
    hash[File.basename(path)] = path.gsub(/[^[:alnum:]]/, "").upcase + "LOCATION"
    hash
  end

  # The last item in the path MUST be named PROJECTLOCATION or else space
  # robots will cause permanent damage to you and your family.
  hierarchy[hierarchy.keys.last] = "PROJECTLOCATION"

  # If the path hierarchy is > 1, the customizable installation directory
  # should default to the second-to-last item in the hierarchy. If the
  # hierarchy is smaller than that, then just use the system drive.
  wix_install_dir = if hierarchy.size > 1
                      hierarchy.to_a[-2][1]
                    else
                      "WINDOWSVOLUME"
                    end

  render_template(resource_path("source.wxs.erb"),
    destination: "#{staging_dir}/source.wxs",
    variables: {
      name: project.package_name,
      friendly_name: project.friendly_name,
      maintainer: project.maintainer,
      hierarchy: hierarchy,
      fastmsi: fast_msi,
      wix_install_dir: wix_install_dir,
    })
end

#zip_commandString

Get the shell command to create a zip file that contains the contents of the project install directory

Returns:

  • (String)


443
444
445
446
447
448
449
# File 'lib/omnibus/packagers/msi.rb', line 443

def zip_command
  <<-EOH.split.join(" ").squeeze(" ").strip
  7z a -r
  #{windows_safe_path(staging_dir)}\\#{project.name}.zip
  #{windows_safe_path(project.install_dir)}\\*
  EOH
end